// We have a lot of c-types in here, stop warning about their names!
#![allow(non_camel_case_types)]

use std::os::raw::{c_char, c_float, c_int};

#[derive(Debug)]
pub enum gender {
    BOY(u8),
    GIRL(u8),
}

#[derive(Debug)]
pub struct student {
    pub num: Option<c_int>,
    pub total: Vec<c_int>,
    pub name: [c_char; 20],
    pub scores: [c_float; 3],
    pub gender: gender,
}

// Default constructor
impl Default for student {
    fn default() -> Self {
        student {
            num: Some(0 as c_int),
            total: vec![],
            name: [0 as c_char; 20],
            scores: [0.0 as c_float; 3],
            gender: gender::BOY(10),
        }
    }
}

impl student {
    pub fn print(&self) {
        println!("print by self method....hahaha...");
    }
}

// 在堆上分配一个rust结构，返回给c
#[no_mangle]
pub extern "C" fn student_new() -> *mut student {
    let new_stu: student = Default::default();
    Box::into_raw(Box::new(new_stu))
}

// 取回rust结构指针，转回rust结构，
// 接受c传递的数据参数，并操作rust结构
#[no_mangle]
pub extern "C" fn student_print(p_stu: *mut student, p_data: *mut c_char, data_len: c_int) {
    if p_stu.is_null() { return; }
    if p_data.is_null() { return; }
    if data_len == 0 { return; }

    // 取结构
    let mut ps = unsafe { Box::from_raw(p_stu) };
    // 取数据
    let slice = unsafe { std::slice::from_raw_parts(p_data, data_len as usize) };
    // 修改数据
    ps.num = Some(slice[1] as c_int + slice[2] as c_int);
    // 调用方法
    ps.print();

    println!("rust side in student_print: {:?}, slice = {:?}", ps, slice);

    // 不释放, 否则这里会free掉内存
    std::mem::forget(ps);
}

// 在堆上分配一个rust结构，赋值，返回给c
#[no_mangle]
pub extern "C" fn student_alice() -> *mut student {
    let mut init_char_array: [c_char; 20] = [0; 20];
    for (dest, src) in init_char_array.iter_mut().zip(b"Alice\0".iter()) {
        *dest = *src as _;
    }
    let scores = [92.5, 87.5, 90.0];
    let alice = student {
        num: Some(1 as c_int),
        total: vec![280],
        name: init_char_array,
        scores,
        gender: gender::GIRL(10),
    };
    Box::into_raw(Box::new(alice))
}

// 由于没有跟c暴露结构体，所以需要写函数来访问字段
#[no_mangle]
pub extern "C" fn student_name(p_stu: *mut student) -> *const c_char {
    if p_stu.is_null() {
        return std::ptr::null();
    }
    unsafe {
        &((*p_stu).name) as *const i8
    }
}

#[no_mangle]
pub extern "C" fn student_free(p_stu: *mut student) {
    if !p_stu.is_null() {
        unsafe {
            println!("rust side print: {:?}", *p_stu);
            // 获得所有权，出作用域自动释放
            Box::from_raw(p_stu)
        };
    }
}
